Skip to content

Conversation

@marcus8448
Copy link
Contributor

@marcus8448 marcus8448 commented Dec 19, 2025

Issue

https://otwarchive.atlassian.net/browse/AO3-7231

Purpose

Upgrade to Rails 8.0.

The upgrade was surprisingly (suspiciously?) straightforward, considering it's a major version bump. I'll add additional notes/comments in a review.

Testing Instructions

TBD, see Jira.

Credit

marcus8448 (he/him)

Looked at all uses of to_time:
* used for duration (difference, so zone vs offset doesn't matter)
* stored in model/DB (rails converts to UTC)
* read from model and sent to akismet (rails converts to UTC)
* or just forced to utc

strict_freshness: I don't see any issues arising from it

Regexp.timeout: in my mind, 1s should be sufficient
Copy link
Contributor Author

@marcus8448 marcus8448 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upgrade script wanted puma.rb, but I ignored it per this comment.

It also wanted to set silence_healthcheck_path in production.rb but it doesn't look like we're using the system anyways. (Noting this just in case we want something silenced, but I doubt it.)


# Library for helping run pt-online-schema-change commands:
gem "departure", "~> 6.8"
gem "departure", "~> 8.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't see anything of note in the changelog. Note that 8.0.1 seems to have not been published to rubygems (if it does get published I would like to pull it in).


group :test do
gem "rspec-rails", "~> 6.0"
gem "rspec-rails", "~> 8.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changelog, nothing stood out as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rails 8.0 now saves the parent model (tag set) before the child model (owned tag set), so we can't always touch it here (after_save). In that case the child model (owned tag set) is going to be saved after anyways, so it's fine to just skip touching.

This change looks to be a side effect of rails/rails#49847, but I think this ordering makes more sense so we were probably just depending on buggy behaviour.
Example, if that explanation doesn't make sense: https://gist.github.com/marcus8448/9459bcc0536f2baf0704cbea91fc9d0b

Copy link
Contributor

@Bilka2 Bilka2 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at your reproduction, did you have a poke around whether any other models are affected by the parent after save/after create being moved around?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Feel free to just say "no" and we will rely on the automatic tests)

Comment on lines -55 to -60
# Raise exceptions for disallowed deprecations.
config.active_support.disallowed_deprecation = :raise

# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from example config, we're not using it anyways.

Comment on lines +55 to +57
# Append comments with runtime information tags to SQL queries in logs.
config.active_record.query_log_tags_enabled = true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example (the /**/ comment):

Tag Load (0.2ms)  SELECT `tags`.* FROM `tags` WHERE `tags`.`name` = 'Uncategorized Fandoms' LIMIT 1 /*action='index',application='Otwarchive',controller='home'*/

Copy link
Contributor

@Bilka2 Bilka2 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://guides.rubyonrails.org/configuring.html#config-active-record-query-log-tags-enabled

When this is set to true database prepared statements will be automatically disabled.

I'd prefer to keep prepared statements in dev by default. But we can still have this line in the config, just commented out

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this file is needed since the app should be able to respond itself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These get copied to the Nginx proxy/frontend servers, so if memory serves, they can respond with this rather than going to the backend for it

gem "rails", "~> 7.2"
gem "rails-i18n"
gem "rails", "~> 8.0.0"
gem "rails-i18n", "~> 8.0", git: "https://github.com/svenfuchs/rails-i18n", ref: "54c1c7c2fdcc311427ec6f1dadd298a60db1ddef"
Copy link
Contributor Author

@marcus8448 marcus8448 Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crashes without an unreleased bugfix. Technically this version targets rails 8.1, but it looks like there are only new keys for 8.1 (none changed/removed) so it shouldn't cause any problems.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +1 to +7
# always enable enqueue_after_transaction_commit
# TODO: remove for rails 8.2! https://github.com/rails/rails/commit/a477a3273c3c71305cc8ae1835638dc75184ad9d
Rails.application.config.after_initialize do
ActiveSupport.on_load(:active_job) do
ActiveJob::Base.enqueue_after_transaction_commit = true
end
end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a manual re-implementation of what the deprecated config.active_job.enqueue_after_transaction_commit = :always did.

The global option is going to be undeprecated and enabled by default in Rails 8.2, so I don't see any reason to not continue with the current behaviour.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very odd... when you write the testing notes, could you see if we can definitely hit this to make sure it works like we expect?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can hit this in manual testing? We didn't for 7.2 where I enabled this value: #5303 (comment)

Copy link
Contributor

@Bilka2 Bilka2 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I haven't tried this, but could we set enqueue after commit directly on ApplicationJob (without all the initializer stuff) since all our jobs descend from that?

# in config/environments, which are processed later.

config.load_defaults 7.2
config.load_defaults 8.0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New framework defaults

###
# Specifies whether `to_time` methods preserve the UTC offset of their receivers or preserves the timezone.
# If set to `:zone`, `to_time` methods will use the timezone of their receivers.
# If set to `:offset`, `to_time` methods will use the UTC offset.
# If `false`, `to_time` methods will convert to the local system UTC offset instead.
#++
Rails.application.config.active_support.to_time_preserves_timezone = :zone

I looked at all the uses of to_time and nothing stood out as being timezone (vs offset) dependent. Anything coming into or out of the database is normalized to UTC anyways and duration calculations shouldn't care about the zone.

###
# When both `If-Modified-Since` and `If-None-Match` are provided by the client
# only consider `If-None-Match` as specified by RFC 7232 Section 6.
# If set to `false` both conditions need to be satisfied.
#++
Rails.application.config.action_dispatch.strict_freshness = true

I don't see anything wrong with this.

###
# Set `Regexp.timeout` to `1`s by default to improve security over Regexp Denial-of-Service attacks.
#++
Regexp.timeout = 1

I think 1s should be plenty of time. Do we have any important complex regexps?

Copy link
Contributor

@Bilka2 Bilka2 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_time: I'm slightly concerned about the use in time_ago_in_words, since it uses Time.now which is not zone aware. Can we switch it to Time.current just for safety? Servers and Rails are in UTC (they weren't always...) so nothing should break, but just in case.

Do we have any important complex regexps?

not sure if complex, but word counting is very important, likely to have very large input and is a regex (ref WordCounter). Maybe test it locally with a very very long chapter and see whether it's an issue?

Copy link
Member

@brianjaustin brianjaustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor things, and I'd like to see testing steps in the ticket before approving. Looks good, generally, though!

@@ -1,5 +1,5 @@
class ErrorsController < ApplicationController
%w[403 404 422 500].each do |error_code|
%w[400 403 404 422 500].each do |error_code|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to the new version, or just completeness?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More for completeness - the new version just wanted /public/400.html (relevant commit), but when making a copy with the archive styling I took a look at what we did for error 500 and saw this controller. If the handling is redundant considering the static pages then I can just delete it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion we should decide on one place to handle it, probably the public file, not here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are multiple error codes here that have been there already, let's do this in a follow-up ticket (something like "standardise HTTP error handling" maybe)

Copy link
Contributor

@Bilka2 Bilka2 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean for this one specific new error 400, just don't add it to the ErrorsController. The other errors are fine to stay here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These get copied to the Nginx proxy/frontend servers, so if memory serves, they can respond with this rather than going to the backend for it

Comment on lines +1 to +7
# always enable enqueue_after_transaction_commit
# TODO: remove for rails 8.2! https://github.com/rails/rails/commit/a477a3273c3c71305cc8ae1835638dc75184ad9d
Rails.application.config.after_initialize do
ActiveSupport.on_load(:active_job) do
ActiveJob::Base.enqueue_after_transaction_commit = true
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very odd... when you write the testing notes, could you see if we can definitely hit this to make sure it works like we expect?

Copy link
Member

@brianjaustin brianjaustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link
Contributor

@Bilka2 Bilka2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marking as requesting changes for the to_time thing, regex complexity manual test and rails-i18n upgrade, rest of my comments are optional to address

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants